﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using TomanuExtensions;

namespace RaytracerLib.MathLib.PolynomialFunction
{
    public static class TreeOperations
    {
        public static Element ReplaceTree(Element a_root, Element a_old, Element a_new)
        {
            Debug.Assert(a_root.AllElements.Contains(a_old.AllElements));

            if (Object.ReferenceEquals(a_root, a_old)) 
                return a_new;

            for (int i = 0; i < a_old.Parent.Children.Count; i++)
            {
                if (Object.ReferenceEquals(a_old.Parent.Children[i], a_old))
                {
                    a_old.Parent.Children[i] = a_new;
                    a_new.Parent = a_old.Parent;
                    a_old.Parent = null;
                    return a_root;
                }
            }

            throw new InvalidOperationException();
        }

        public static Element RemoveSymbol(Element a_root, SymbolElement a_to_remove)
        {
            Debug.Assert(a_root.AllElements.Contains(a_to_remove));

            BinaryOperator parent = a_to_remove.Parent as BinaryOperator;

            a_to_remove.Parent = null;

            if (Object.ReferenceEquals(parent.Left, a_to_remove))
                return Replace(a_root, parent, parent.Right);
            else
                return Replace(a_root, parent, parent.Left);
        }

        private static Element Replace(Element a_root, Element a_old, Element a_new)
        {
            if (Object.ReferenceEquals(a_root, a_old))
            {
                a_new.Parent = null;
                return a_new;
            }

            a_old.Parent.Children.Replace(a_old, a_new);
            a_new.Parent = a_old.Parent;
            a_old.Parent = null;

            return a_root;
        }

        public static Element InsertBefore(Element a_root, Element a_element, UnaryOperator a_operator)
        {
            Debug.Assert(a_root.AllElements.Contains(a_element));
            Debug.Assert(a_operator.Child == null);

            a_root = Replace(a_root, a_element, a_operator);
            a_operator.Child = a_element;
            return a_root;
        }

        public static Element Remove(Element a_root, UnaryOperator a_operator)
        {
            Debug.Assert(a_root.AllElements.Contains(a_operator));

            a_root = Replace(a_root, a_operator, a_operator.Child);
            a_operator.Child = null;
            return a_root;
        }

        public static Element Remove(Element a_root, Element a_element)
        {
            Debug.Assert(!Object.ReferenceEquals(a_root, a_element));

            SymbolElement symbol = a_element as SymbolElement;

            if (symbol != null)
                return RemoveSymbol(a_root, symbol);

            Element parent = a_element.Parent;

            for (int i=0; i<parent.Children.Count; i++)
            {
                Element new_el = parent.Children[i];
                if (!Object.ReferenceEquals(new_el, a_element))
                {
                    a_root = Replace(a_root, parent, new_el);
                    a_element.Parent = null;
                    return a_root;
                }
            }

            throw new InvalidOperationException();
        }

        public static Element InsertBefore(Element a_root, Element a_old_and_new_child, MulOperator a_new, 
            Element a_new_child)
        {
            Debug.Assert(a_root.AllElements.Contains(a_old_and_new_child));

            a_root = Replace(a_root, a_old_and_new_child, a_new);
            a_new.Left = a_new_child;
            a_new.Right = a_old_and_new_child;
            return a_root;
        }

        public static Element InsertBefore(Element a_root, Element a_old_and_new_child, PowOperator a_new,
            Element a_new_child)
        {
            Debug.Assert(a_root.AllElements.Contains(a_old_and_new_child));

            a_root = Replace(a_root, a_old_and_new_child, a_new);
            a_new.Left = a_old_and_new_child;
            a_new.Right = a_new_child;
            return a_root;
        }

        public static void SwitchChildren(BinaryOperator a_bo)
        {
            Element temp = a_bo.Left;
            a_bo.Left = a_bo.Right;
            a_bo.Right = temp;
        }

        public static void Switch(Element a_el1, Element a_el2)
        {
            a_el1.Parent.Children.Replace(a_el1, a_el2);
            a_el2.Parent.Children.Replace(a_el2, a_el1);
            Operator parent = a_el1.Parent;
            a_el1.Parent = a_el2.Parent;
            a_el2.Parent = parent;
        }
    }
}
